package com.atguigu.gmall.order.service;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.cart.pojo.Cart;
import com.atguigu.gmall.common.bean.ResponseVo;
import com.atguigu.gmall.common.exception.OrderException;
import com.atguigu.gmall.oms.vo.OrderItemVo;
import com.atguigu.gmall.oms.vo.OrderSubmitVo;
import com.atguigu.gmall.order.feign.*;
import com.atguigu.gmall.order.interceptor.LoginInterceptor;
import com.atguigu.gmall.order.pojo.UserInfo;
import com.atguigu.gmall.order.vo.OrderConfirmVo;
import com.atguigu.gmall.pms.entity.SkuAttrValueEntity;
import com.atguigu.gmall.pms.entity.SkuEntity;
import com.atguigu.gmall.pms.entity.SkuImagesEntity;
import com.atguigu.gmall.sms.vo.ItemSaleVo;
import com.atguigu.gmall.ums.entity.UserAddressEntity;
import com.atguigu.gmall.ums.entity.UserEntity;
import com.atguigu.gmall.wms.entity.WareSkuEntity;
import com.atguigu.gmall.wms.vo.SkuLockVo;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class OrderService {
    @Autowired
    private GmallUmsClient UmsClient;
    @Autowired
    private GmallCartClient cartClient;
    @Autowired
    private GmallPmsClient pmsClient;
    @Autowired
    private GmallSmsClient smsClient;
    @Autowired
    private GmallWmsClient wmsClient;
    @Autowired
    private GmallOmsClient omsClient;
    @Autowired
    StringRedisTemplate redisTemplate;
    @Autowired
    private RabbitTemplate rabbitTemplate;

    private static final String KEY_PREFIX="order:token:";

    //订单确认页可以异步编排
    public OrderConfirmVo confirm() {

/*
    List<UserAddressEntity> addresses  地址列表ums_user_address
    1.根据用户id查询用户的收货地址列表

    Integer bounds;//购物积分,京豆ums_user
    2.根据用户id查询用户信息（积分）V

    List<OrderItemVo> orderItems送货清单
    3.根据用户id查询该用户选中的购物车记录（skuId count）
    4.根据skuId查询sku信息  V
    5.根据skuId查询销售属性  V
    6.根据skuId查询库存信息 V
    7.根据skuId查询商品的营销信息 V
*/

        // 从拦截器中获取用户id
        UserInfo userInfo = LoginInterceptor.getUserInfo();
        Long userId = userInfo.getUserId();
        OrderConfirmVo orderConfirmVo = new OrderConfirmVo();
//        1.根据用户id查询用户的收货地址列表
        ResponseVo<List<UserAddressEntity>> addressesResponseVo = UmsClient.queryAddressesByUserId(userId);
        List<UserAddressEntity> addressEntities = addressesResponseVo.getData();
        orderConfirmVo.setAddresses(addressEntities);
//        2.根据用户id查询用户信息（积分）V
        ResponseVo<UserEntity> userEntityResponseVo = UmsClient.queryUserById(userId);
        UserEntity userEntity = userEntityResponseVo.getData();
        if (userEntity!=null) {
            Integer integration = userEntity.getIntegration();
            orderConfirmVo.setBounds(integration);
        }

//         查询送货清单
        //3.根据用户id查询该用户选中的购物车记录（skuId count）
        ResponseVo<List<Cart>> cartsVo = cartClient.queryCheckedCarts(userId);
        List<Cart> carts = cartsVo.getData();
        if (CollectionUtils.isEmpty(carts)) {
            throw new OrderException("你没有选中的购物车记录");
        }
        List<OrderItemVo> orderItems = carts.stream().map(cart -> {
            OrderItemVo itemVo = new OrderItemVo();
            // 只取skuId count
            Long skuId = cart.getSkuId();
            itemVo.setCount(cart.getCount());
            itemVo.setSkuId(skuId);
            // 根据skuId查询sku
            ResponseVo<SkuEntity> skuEntityResponseVo = pmsClient.querySkuById(skuId);
            SkuEntity skuEntity = skuEntityResponseVo.getData();
            if (skuEntity != null) {
                itemVo.setDefaultImage(skuEntity.getDefaultImage());
                itemVo.setTitle(skuEntity.getTitle());
                itemVo.setPrice(skuEntity.getPrice());
                itemVo.setWeight(skuEntity.getWeight());
            }
            //根据skuId查询该商品的销售属性
            ResponseVo<List<SkuAttrValueEntity>> saleAttrResponseVo = pmsClient.querySaleAttrValueBySkuId(skuId);
            List<SkuAttrValueEntity> skuAttrValueEntities = saleAttrResponseVo.getData();
            itemVo.setSaleAttrs(skuAttrValueEntities);
            //根据skuId查询该库存信息
            ResponseVo<List<WareSkuEntity>> wareResponseVo = wmsClient.queryWareSkusBySkuId(skuId);
            List<WareSkuEntity> wareSkuEntities = wareResponseVo.getData();
            if (!CollectionUtils.isEmpty(wareSkuEntities)) {
                itemVo.setStore(wareSkuEntities.stream().anyMatch(WareSkuEntity -> WareSkuEntity.getStock() - WareSkuEntity.getStockLocked() > 0));
            }

            // 根据skuId查询营销信息
            ResponseVo<List<ItemSaleVo>> salesResponseVo = smsClient.querySalesBySkuId(skuId);
            List<ItemSaleVo> itemSaleVos = salesResponseVo.getData();
            itemVo.setSales(itemSaleVos);
            return itemVo;
        }).collect(Collectors.toList());
        orderConfirmVo.setOrderItems(orderItems);

        // 防重：浏览器一份  redis一份
        //IdWorker.getIdStr();分布式的唯一标识mybatis plus提供的
        //分布式id实现：
        //用户手机号+时间戳（毫秒值）
        String orderToken = IdWorker.getIdStr();
        orderConfirmVo.setOrderToken(orderToken);
        redisTemplate.opsForValue().set(KEY_PREFIX+orderToken,orderToken,1, TimeUnit.HOURS);

        return orderConfirmVo;
    }

    public void submit(OrderSubmitVo submitVo) {
        // 1.防重：页面orderToken  redis，为了保证原子性使用lua脚本
        String orderToken = submitVo.getOrderToken();
        if (StringUtils.isBlank(orderToken)) {
            throw new OrderException("非法提交！！");
        }
        String script ="if(redis.call('get',KEYS[1])==ARGV[1]) then return redis.call('del',KEYS[1]) else return 0 end";
        Boolean flag = redisTemplate.execute(new DefaultRedisScript<>(script, Boolean.class),
                Arrays.asList(KEY_PREFIX + orderToken), orderToken);
        if (!flag) {
            throw new OrderException("请不要重复提交！");
        }
        // 2.验总价
        BigDecimal totalPrice = submitVo.getTotalPrice();
        // 获取页面上的送货清单List<OrderItemVo> orderItems;//送货清单
        List<OrderItemVo> items = submitVo.getItems();
        if (CollectionUtils.isEmpty(items)) {
            throw new OrderException("您没有要购买的商品");
        }
        //遍历OrderItemVo，获取每个商品的skuId，查询每个sku的价格，并加在一起
        // 获取数据库中的实时总价
        BigDecimal currentTotalPrice = items.stream().map(orderItemVo -> {
            ResponseVo<SkuEntity> skuEntityResponseVo = pmsClient.querySkuById(orderItemVo.getSkuId());
            SkuEntity skuEntity = skuEntityResponseVo.getData();
            if (skuEntity != null) {
                return skuEntity.getPrice().multiply(orderItemVo.getCount());// 实时价格小计
            }
            return new BigDecimal(0);
        }).reduce((a, b) -> a.add(b)).get();
        if(currentTotalPrice.compareTo(totalPrice)!=0){
            throw new OrderException("页面已过期，请刷新后重试");
        }
        // 3.验库存并锁定库存,需要把送货清单里的货都遍历一遍
        List<SkuLockVo> lockVos = items.stream().map(orderItemVo -> {
            SkuLockVo skuLockVo = new SkuLockVo();
            skuLockVo.setSkuId(orderItemVo.getSkuId());
            skuLockVo.setCount(orderItemVo.getCount().intValue());
            return skuLockVo;
        }).collect(Collectors.toList());
        
        ResponseVo<List<SkuLockVo>> wareResponseVo = wmsClient.checkAndLock(lockVos, orderToken);
        List<SkuLockVo> skuLockVoList = wareResponseVo.getData();
        // 4.下单
        UserInfo userInfo = LoginInterceptor.getUserInfo();
        Long userId = userInfo.getUserId();
        try {
            omsClient.saveOrder(submitVo, userId);
            // 定时关单
            rabbitTemplate.convertAndSend("ORDER_EXCHANGE", "order.ttl", orderToken);
        } catch (Exception e) {
            // 调用失败了或者时oms宕机了  响应失败了
            //1.调用失败  订单还没有创建，解锁库存（wms）
           // 2.响应失败  订单已经有了，将此订单状态标记为无效订单“status=5为无效订单”（oms）
            //一个发送方，两个接收方（wms，oms）
            rabbitTemplate.convertAndSend("ORDER_EXCHANGE", "order.failure", orderToken);
            throw new OrderException("服务器错误。。。");
        }

        // 5.异步删除购物车中对应的记录
        //哪个人的购物车，与购物车中的哪几条记录List<Long> skuIds
        //只传userId，通过userId找到购物车中被选择的记录，然后删除，这样不行
        Map<String, Object> message = new HashMap<>();
        message.put("userId", userId);
        List<Long> skuIds = items.stream().map(OrderItemVo::getSkuId).collect(Collectors.toList());
        //JSON.toJSONString(skuIds)方便解析
        //convertAndSend是将数据二进制再反序列化，容易混乱，所以转为json字符串好转换
        message.put("skuIds", JSON.toJSONString(skuIds));
        this.rabbitTemplate.convertAndSend("ORDER_EXCHANGE", "cart.delete", message);
    }
}
